/*
* For QuoteSpeed II
* David Li 20100125
*/
if (!Util) {
	var Util = {};
}

/*data grid*/
Util.DataGrid = function(container, data, oConfig, callback){
    this.el = null;
    this.container = $(container);
    this.rows = {};
    this.header = null;
    this.body = null;
    this.data = data || [];
    this.DOMMap = {};
    this.lastSortCellKey = '';
    this.lastSortOrder = '';
    this._emptyStr = '';
    this._rowOnePage = 10;
    this.selectedRow = null;
    this.selectedRows = {};
    this.cfg = {
        column:[],
        indexColKey:'id',
        colMinWidth:20,
        width:300,
        height:200,
        //fixSize:true,
        headerRow:true,
        headerContextMenu:false,
        headerContainer: null,
        outSideHeader: false,
        sortable:true,
        cellResize:true,
        cellRearrange:true,
        highlightColumn:false,
        removeButton:true,
        alternateRow:false,
        lazyLoad: false,
        rowHeight: 20,
        delayResize: true,
        autoWidth: false,
        autoHeight: false,
        showYScrollbar: true,
        rowLayout: 'list', //grid
        htmlLayout:'div',//div/table
        rowDragable: false,
        rowDropable: false,
        rowDragDropGroup: '',
        rowSelectable: true,
        rowInvertSelect: true,
        multiSelect: false,
        sortDataType: 'string',
        groupDragable: false,
        groupDropable: false,
        groupAutoSort: false,
        groupDataIndex: null //only for Div layer
    };
    $.extend(true, this.cfg, oConfig);
    this.cb = {
        onRowDoubleClick:null,
        onRowContextmenu:null,
        onRowRemove:null,
        onRowDragStart:null,  //e, proxy, row;
        onRowRearrange:null,  //function(Row, insertBeforeRow){}
        onRowSelected:null,  //function(e, Row){}
        onRowMouseover:null, //function(e, Row){}
        onRowMouseout:null, //function(e, Row){}
        afterRowRender:null, //function(Row){}
        afterResizeCol:null, //
        onSortedbyCell:null,//exec the curstom event.
        onGroupDragStart: null, //function(proxy, group){}
        beforeGroupDrop: null, //function(proxy, group, toGroup){},
        afterGroupDrop: null, //function(proxy, group, toGroup){},
        onCellSort:null,  //function(cellKey, order){}
        onCellRemove:null,  //function(cellKey){}
        onCellRearrange:null,  //function(cell, insertBeforeCell){}
        onCellMouseover:null,  //function(e, cell, row){}
        onCellMouseout:null,  //function(e, cell, row){}
        onCellClick:null, //funciton(e,cell,row){}
        onScroll:null,   //function(scrollLeft, scrollTop){}
        onScrollEnd:null,   //function(scrollLeft, scrollTop){}
        afterRebindData:null
    };
    $.extend(this.cb, callback);
    this._freezdTitileCb = function(){};
    this.dataGroup = null;
    this.HTML = '<div class="rtq-grid"><div class="rtq-grid-hd"></div><div class="rtq-grid-scroll"><div class="rtq-grid-bd"></div></div></div>';
    this.init();
};
Util.DataGrid.prototype={
    init:function(){
        var self = this;
        this.el = $(this.HTML);
        this.body = this.el.find('.rtq-grid-bd');
        this.headerEl = this.el.find('.rtq-grid-hd');
        
        if(this.cfg.groupDataIndex){
            this.cfg.htmlLayout = 'div';
            this.dataGroup = new Util.dataGroup(this.body, {
                dragable: this.cfg.groupDragable,
                dropable: this.cfg.groupDropable,
                autoSort: this.cfg.groupAutoSort,
                group: 'dataGridGroup'
            }, this);
        }

        if(this.cfg.autoWidth){
            this.el.addClass('rtq-grid-auto');
            this.cfg.width = '100%';
            var rowWidth = 0;
        }
        if(this.cfg.autoHeight){
            this.el.addClass('rtq-grid-auto-h');
            this.cfg.height = '100%';
        }
        if (this.cfg.headerContainer) {
            this.headerEl.appendTo(this.cfg.headerContainer);
            this.cfg.outSideHeader = true;
            //this.el.width(this.grid.container.width()||this.grid.cfg.width||this.grid.el.width());
            this.cfg.headerContainer.addClass('rtq-grid');
        }
        
        this._createHeader();
        if(this.cfg.htmlLayout == 'table'){
            this.el.addClass('rtq-grid-tblyr');
            this._createTableBody();
        }
        this.el.appendTo(this.container);
        
        this.scrollPanel = new Util.ScrollPanel(this.el.find('.rtq-grid-scroll'), {
            showX:!this.cfg.autoWidth,
            showY:!this.cfg.autoHeight,
            delayRun: this.cfg.delayResize
        }, {
            onScroll: function(l, t){
				self._trigger('scroll', [l, t]);
                self._freezdTitileCb(l, t);
            },
            onScrollEnd: function(l, t){
				if (self.scrollPanel &&(self.scrollPanel.scrollbarY && self.scrollPanel.scrollbarY.isShow)
				    ||(self.scrollPanel.scrollbarX && self.scrollPanel.scrollbarX.isShow)) {
					if (self.cfg.lazyLoad) {
						self._lazyLoad(t);
					}
					self._trigger('scrollend', [l, t]);
				}
            }
        });
        this.resize(this.cfg.width, this.cfg.height);
    },
    _createHeader: function(){
        if (this.cfg.headerRow) {
            var self = this;
            if(this.header){
                this.header.clear();
            }
            this.header = new Util.GridHeader(this, {
                afterResize: function(cellKey, cellWidth){
                    self.resizeCol.apply(self, arguments);
                },
                afterColSort: function(cellKey, index){
                    self._appendColTo.apply(self, arguments);
                },
                onCellClick: self.cfg.sortable ? function(cellKey){
                    self.sortByCell.apply(self, arguments);
                }: null
            });
            
            if (this.cfg.headerContextMenu) {
                Util.DataGridAddOn.HeaderContextmenu(this);
            }
            
            this._freezdTitileCb = function(l, t){
                self.header.el.scrollLeft(l);
            };
			if (this.cfg.headerRow) {
	            this.header.ellipsisDom();
	        }
        }else{
            this.headerEl.height(0); //fix ie7 bug
        }
    },
    getVisibleRow: function(scrollTop, viewHeight, viewWidth){
        var scrollTop = scrollTop || this.scrollPanel.scrollTop;
        var viewHeight = viewHeight || this.scrollPanel.el[0].offsetHeight;
        var viewWidth = viewWidth || this.scrollPanel.el[0].offsetWidth;
        var rowElements = this._getAllRowsDOM();
        var rowCount = this.getRowLength();
        if (this.cfg.rowLayout == 'list') {
            var rowIdx = Math.round(scrollTop / this.body[0].offsetHeight * rowCount) - 2;
            this._rowOnePage = Math.round(viewHeight / this.cfg.rowHeight) + 2 * 2;
        }
        else {
            var rowH = rowElements.eq(0).height();
            var rowW = rowElements.eq(0).width();
            var rowIdx = Math.round(scrollTop / this.body[0].offsetHeight * rowCount);
            this._rowOnePage = Math.ceil(viewHeight / rowH) * Math.ceil(viewWidth / rowW);
        }
        var l = Math.min(rowCount, rowIdx + this._rowOnePage);
        var i = Math.max(rowIdx, 0);
        var key = '';
        var rows = {};
        for (; i < l; i++) {
            key = rowElements.eq(i)[0].getAttribute('rowid');
            rows[key] = this.rows[key];
        }
        return rows;
    },
    _lazyLoad: function(scrollTop){
        var visibleRows = this.getVisibleRow(scrollTop);
        for (var i in visibleRows) {
            visibleRows[i].renderDOM();
        }
        this._resizeBody();
    },
    _createTableBody:function(){
        this.body.append($('<table class="rtq-grid-table"><tbody></tbody></table>'));
    },
    _getBody:function(rowData, prepend){
        if (this.cfg.htmlLayout == 'table') {
            return this.body.children('table').children('tbody')[0];
        }
        else {
            if (this.cfg.groupDataIndex) {
                var d = rowData[this.cfg.groupDataIndex];
                return this.dataGroup.getGroupBox(d, prepend).body[0];
            }
            else {
                return this.body[0];
            }
        }
    },
    _bindData:function(){
        if (this.cfg.groupDataIndex) {
        
            for (var i = 0, l = this.data.length; i < l; i++) {
                this._addRow(this._getBody(this.data[i]), this.data[i]);
            }
            
        }
        else {
            var frag = document.createDocumentFragment(); //For Performance
            for (var i = 0, l = this.data.length; i < l; i++) {
                this._addRow(frag, this.data[i]);
            }
            this._getBody().appendChild(frag);
        }
        this._setAlternateRow(100);
        
        Util.DelayRun('resize', this._resizeBody, 0, this);
        
        if (this.cfg.lazyLoad) {
            this._lazyLoad(this.scrollPanel.scrollTop);
        }
        
        /*
        this._delayRun('resize', function(){
            self._resizeBody();
            //self.body.css('visibility', 'visible');
        }, 0);
        */
        
    },
    reBindData:function(data){
        this.empty();
        this.data = data;
        this._rowStartIdx = 0;
        this._rowEndIdx = this._rowOnePage;
        this._bindData();
        this._trigger('afterbind', [this.rows]);
		this._ellipsisDom();
    },
	_ellipsisDom:function(rowid){
        //ellipsis some char
        for (var i = 0, l = this.cfg.column.length; i < l; i++) {
            var col=this.cfg.column[i];
			if (rowid) {
			    var row = this.rows[rowid];
				row.cells[col["dataIndex"]].ellipsisDom();
			}
			else {
				for (var k in this.rows) {
					var row = this.rows[k];
					row.cells[col["dataIndex"]].ellipsisDom();
				}
			}
        }
    },
    _addRow:function(container, data, id, idx){
        var id = id||data[this.cfg.indexColKey]||data[this.cfg.column[0].dataIndex];
        if(!id){return;}

        var self = this;
        if(this.cb.onCellMouseover){
            
        }
        this.rows[id] = new Util.DataRow(container, this.cfg.column, data, id, function(id){
            self._trigger('rowremove', [id, self.rows[id]]);
            self.removeRow(id);
        }, this.cfg.htmlLayout == 'table' ? 'tr' : 'div', idx == 0 ? true : false, this.cfg.lazyLoad ? true : false, {
            mouseover: this.cb.onRowMouseover,
            mouseout: this.cb.onRowMouseout,
            cellMouseover: this.cb.onCellMouseover,
            cellMouseout: this.cb.onCellMouseout,
			cellClick: this.cb.onCellClick
        });
        
        
        if (this.cfg.rowDragable) {
            this.rows[id].dragObj = new Util.Dragable(this.rows[id].el, {
                onDragStart: function(e, proxy){
                    proxy.html(self.rows[id].id);
                    proxy.dataObj = self.rows[id];
                    proxy.gridObj = self;

                    self._trigger('rowdragstart', [e, proxy, self.rows[id]]);
                },
                onDrag: function(e, proxy){
                //proxy.html(self.rows[id].id);
                }
            }, {
                group: this.cfg.rowDragDropGroup
            });
        }
        if (this.cfg.rowDropable) {
            this.rows[id].dropObj = new Util.Dropable(this.rows[id].el, {
                onEnter: function(e, proxy){
                    if (proxy.gridObj && proxy.gridObj == self) {
                        if (self.cfg.groupDataIndex && proxy.dataObj.data[self.cfg.groupDataIndex] != self.rows[id].data[self.cfg.groupDataIndex]) {
                            proxy.removeClass('rtq-dd-drop-yes');
                            return;//dont allow drop in different group
                        }
                        self.rows[id].el.addClass('rtq-grid-row-drop');
                    }
                },
                onDrop: function(e, proxy){
                    if (proxy.gridObj && proxy.gridObj == self) {
                        self.rows[id].el.removeClass('rtq-grid-row-drop');
                        if (self.cfg.groupDataIndex && proxy.dataObj.data[self.cfg.groupDataIndex] != self.rows[id].data[self.cfg.groupDataIndex]) {
                            return;//dont allow drop in different group
                        }
                        if (proxy.dataObj && proxy.dataObj != self.rows[id]) {
                            self._appendRowBrfore(proxy.dataObj, self.rows[id]);

                            self._trigger('rowrearrange', [proxy.dataObj, self.rows[id]]);
                        }
                    }
                },
                onOut: function(e, proxy){
                    if (proxy.gridObj && proxy.gridObj == self) {
                        self.rows[id].el.removeClass('rtq-grid-row-drop');
                    }
                }
            }, {
                group: this.cfg.rowDragDropGroup
            });
        }
        if(this.cb.onRowDoubleClick){
            this.rows[id].el.bind('dblclick.DataGrid', function(e){
                self.cb.onRowDoubleClick(e, self.rows[id]);
            });
        }
        if(this.cb.onRowContextmenu){
            this.rows[id].el.bind('contextmenu.DataGrid', function(e){
                self.cb.onRowContextmenu(e, self.rows[id]);
            });
        }
        if(this.cfg.rowSelectable){
            this.rows[id].el.bind('click.DataGrid', function(e){
                self.selectRow(id, e);
            });
        }
        
        this._clearSort();
        
        this.DOMMap[id] = this.rows[id].DOMMap;
		return id;
    },
    addRow:function(data, id, idx){
        var id=this._addRow(this._getBody(data, idx==0?true:false), data, id, idx);  //idx==0?true:false for group prepend
        /*
        if(idx>-1){
            this._appendRowTo(this.rows[id||data[this.cfg.indexColKey]||data[this.cfg.column[0].dataIndex]], idx);
        }
        */
		this._ellipsisDom(id);
        this._setAlternateRow(100); //for Performance
        Util.DelayRun('resize', this._resizeBody, 0, this);
    },
    removeRow:function(id){
        if (this.rows[id]) {
            if (this.dataGroup) {
                var groupKey = this.rows[id].cells[this.cfg.groupDataIndex].el.text() || Util.DG_emptyGroupKey;
                if (this.dataGroup.groups[groupKey] && this.dataGroup.getIDs(groupKey).length <= 1) {
                    this.dataGroup._removeGroup(groupKey);
                }
            }
            this.rows[id].remove();
            this.rows[id] = null;
            this.DOMMap[id] = null;
            delete this.rows[id];
            delete this.DOMMap[id];
            
            this._setAlternateRow(100); //for Performance
            Util.DelayRun('resize', this._resizeBody, 0, this);
        }
    },
    removeColumn:function(cellKey){
        for(var i =0;i<this.cfg.column.length;i++){
            if(this.cfg.column[i].dataIndex == cellKey){
                this.cfg.column.splice(i, 1);
                break;
            }
        }
        for(var r in this.rows){
            this.rows[r].removeCell(cellKey);
        }
        this.header.row.removeCell(cellKey);
        this._trigger('cellremove', [cellKey]);
        Util.DelayRun('resize', this._resizeBody, 100, this);
    },
    empty:function(){
        //this.body.empty();
        //this._getBody().innerHTML = '';//+80% Pfm
        this.body[0].innerHTML = '';
        this.selectedRows = {};
        this.selectedRow = null;
        if(this.cfg.htmlhtmlLayout == 'table'){
            this._createTableBody();
        }
        
        for(var k in this.rows){
            this.rows[k].clear();
        }
        delete this.rows;
        delete this.DOMMap;
        this.rows = {};
        this.DOMMap = {};
        if (this.dataGroup) {
            this.dataGroup.empty();
        }
        
        this._setAlternateRow(100); //for Performance
        Util.DelayRun('resize', this._resizeBody, 100, this);
    },
    selectRow: function(rowid, e){
        e = e || null;
        var row = this.rows[rowid];
        if (row) {
            if (this.cfg.multiSelect) {
                if (this.selectedRows[id]) {
                    row.el.removeClass('rtq-grid-row-selected');
                    this.selectedRows[id] = null;
                    delete this.selectedRows[id];
                }
                else {
                    row.el.addClass('rtq-grid-row-selected');
                    this.selectedRows[id] = row;
                }
            }
            else {
                if (this.selectedRow && this.selectedRow.el) {
                    this.selectedRow.el.removeClass('rtq-grid-row-selected');
                    if (this.selectedRow == row && this.cfg.rowInvertSelect) {
                        this.selectedRow = null;
                    }
                    else {
                        row.el.addClass('rtq-grid-row-selected');
                        this.selectedRow = row;
                        this._trigger('rowselected', [e, this.selectedRow]);
                    }
                }
                else {
                    row.el.addClass('rtq-grid-row-selected');
                    this.selectedRow = row;
                    this._trigger('rowselected', [e, this.selectedRow]);
                }
            }
        }
    },
    _clearSelect: function(){
        if (this.cfg.multiSelect) {
            for (var i in this.selectedRows) {
                this.selectedRows[i].el.removeClass('rtq-grid-row-selected');
                this.selectedRows[i] = null;
                delete this.selectedRows[i];
            }
        }else{
            if(this.selectedRow){
                this.selectedRow.el.removeClass('rtq-grid-row-selected');
                this.selectedRow = null;
            }
        }
    },
    _appendRowTo:function(row, index){
        this._getBody(row.data).insertBefore(row.el[0], this._getAllRowsDOM().eq(index)[0]);
        this._clearSort();
        this._setAlternateRow();
    },
    _appendRowBrfore:function(row, targetRow){
        this._getBody(row.data).insertBefore(row.el[0], targetRow.el[0]);
        this._clearSort();
        this._setAlternateRow();
    },
    _appendColTo:function(cellKey, index){
        for(var k in this.rows){
            this.rows[k].appendCellTo(cellKey, index);
        }
        //re-arrange column
        var cols = this.cfg.column;
        var oldIdx = 0;
        for(var i=0;i<cols.length;i++){
            if(cellKey == cols[i].dataIndex){
                oldIdx = i;
                break;
            }
        }
        var c = cols[oldIdx];
        cols.splice(oldIdx, 1);
        cols.splice(oldIdx>index?index:index-1, 0, c);
        
        this._trigger('cellrearrange', [cellKey, index]);
    },
    appendColTo:function(cellKey, index){
        this.header.appendCellTo(cellKey, index);
        this._appendColTo(cellKey, index);
    },
    highlightColumn: function(cellKey, isHighlight){
        isHighlight = isHighlight === false ? false : true; //default to true
        var rows = this.getVisibleRow();
        if (isHighlight) {
            for (var r in rows) {
                rows[r].cells[cellKey].el.addClass('rtq-grid-cell-highlight');
            }
        }else{
            for (var r in rows) {
                rows[r].cells[cellKey].el.removeClass('rtq-grid-cell-highlight');
            }
        }
    },
    resize:function(w, h){
        if(w>0){this.el.width(w);}
        if(h>0){this.el.height(h);}

        if (w > 0 || h > 0) {
            //this._resizeBody();
            this._resizeBody(w,h);
        }
    },
	ellipsisDom:function(){
		if (this.cfg.headerRow){
            this.header.ellipsisDom();
        }
		this._ellipsisDom();
	},
    _resizeBody: function(w, h){
		var rowW = this._getRowWidth();
        var sw = w || this.el.outerWidth();//use outerWidth to include border width
        var sh = (h || this.el.height()) - (this.cfg.outSideHeader?0:this.cfg.headerRow?this.header.el.outerHeight(true):0);
        if (!this.cfg.autoWidth) {
            if (rowW > 0 && this.cfg.rowLayout == 'list') {
                this.body.width(rowW + 2);
            }
            else {
                this.body.width(sw);
            }
        }
        if (sw < 0 || sh < 0) {
            
        }else{
			this.scrollPanel.reSize(sw, sh);
            if (this.cfg.autoWidth && this.header) { //fix header width in autoWidth mode
                var self = this;
                setTimeout(function(){ //delay for the scrollpanel processing
                    (function(){
                        if (this.scrollPanel.scrollbarY.isShow) {
                            this.header.el[0].style.marginRight = '12px';
                        }
                        else {
                            this.header.el[0].style.marginRight = '0';
                            this.scrollPanel.target[0].style.marginRight = '0';
                        }
                    }).call(self);
                }, 100)
            }
        }
        
    },
    _getRowWidth:function(){
        var w = 0;
        if (this.cfg.headerRow) {
            w = this.header.row.getRowWidth();
        }
        else {
            for (var k in this.rows) {
                w = this.rows[k].getRowWidth();
                break;
            }
        }
        return w>0?w:0;
    },
    _setAlternateRow:function(delay){
        if (this.cfg.alternateRow) {
            var self = this;
            delay = delay || -1;
            var fn = function(){
                self._getAllRowsDOM().each(function(i){
                    if (i % 2 == 0) {
                        $(this).removeClass('rtq-grid-row-alt');
                    }
                    else {
                        $(this).addClass('rtq-grid-row-alt');
                    }
                });
            }
            if (delay < 0) {
                fn();
            }
            else {
                Util.DelayRun('altRow', fn, delay, this);
            }
        }
    },
    _getAllRowsDOM:function(){
        return this.body.find('.rtq-grid-row');
    },
    getIDs:function(){
        var a = [];
        var self = this;
        this._getAllRowsDOM().each(function(i){
            a.push(this.getAttribute('rowid'));
        });
        return a;
    },
    getRows:function(){
        return this.rows;
    },
    getRowLength:function(){
        var i = 0;
        for(var r in this.rows){
            i++
        }
        return i;
    },
    resizeCol:function(cellKey, w, h){
        //save width into column config
        for(var i=0;i<this.cfg.column.length;i++){
            if(this.cfg.column[i].dataIndex == cellKey){
                this.cfg.column[i].width = w;
                break;
            }
        }
        for(var k in this.rows){
            this.rows[k].resizeCell(cellKey, w, h);
        }
        this._resizeBody();
		if (this.cb.afterResizeCol) {
            this.cb.afterResizeCol(cellKey,w);
        }
    },
    _dateTimeConvert:function(data,format){
        if(!format){
            return data;
        }
        var d,monthMap={'JAN':1,'FEB':2,'MAR':3,'APR':4,'MAY':5,'JUN':6,'JUL':7,'AUG':8,'SEP':9,'OCT':10,'NOV':11,'DEC':12};
        switch(format){
            case 'M-d-yyyy':{
                d=data.split('-');
                return new Date(parseInt(d[2],10),parseInt(d[0],10)-1,parseInt(d[1],10));
            }break;
            case 'M/d/yyyy':{
                d=data.split('/');
                return new Date(parseInt(d[2],10),parseInt(d[0],10)-1,parseInt(d[1],10));
            }break;
            case 'yyyy-M-d':{
                d=data.split('-');
                return new Date(parseInt(d[0],10),parseInt(d[1],10)-1,parseInt(d[2],10));
            }break;
            case 'yyyy/M/d':{
                d=data.split('/');
                return new Date(parseInt(d[0],10),parseInt(d[1],10)-1,parseInt(d[2],10));
            }break;
            case 'MMM':{
                return monthMap[data.toUpperCase()];
            }break;
            case 'h:mm a':{
                var a=data.substring(data.length-2,data.length);
                d=data.split(" ")[0].split(":");
                if(a.toUpperCase()=='PM'&&d[0]!=12){//12:01 pm
                    return (parseInt(d[0],10)+12)*60+parseInt(d[1]);
                }else{
                    return parseInt(d[0],10)*60+parseInt(d[1]);
                }
            }break;
            case 'HH:mm':{
                d=data.split(":");
                return parseInt(d[0],10)*60+parseInt(d[1]);
            }break;
            case 'M-d-yyyy, HH:mm':{
                data=data.replace(" ","");
                d=data.split(',');
                var dt=d[0].split('-'),tm=d[1].split(':');
                return new Date(parseInt(dt[2],10),parseInt(dt[0],10)-1,parseInt(dt[1],10),parseInt(tm[0],10),parseInt(tm[1],10));              
            }break;
            case 'M/d/yyyy, HH:mm':{
                data=data.replace(" ","");
                d=data.split(',');
                var dt=d[0].split('/'),tm=d[1].split(':');
                return new Date(parseInt(dt[2],10),parseInt(dt[0],10)-1,parseInt(dt[1],10),parseInt(tm[0],10),parseInt(tm[1],10));              
            }break;
            case 'yyyy-M-d, HH:mm':{
                data=data.replace(" ","");
                d=data.split(',');
                var dt=d[0].split('-'),tm=d[1].split(':');
                return new Date(parseInt(dt[0],10),parseInt(dt[1],10)-1,parseInt(dt[2],10),parseInt(tm[0],10),parseInt(tm[1],10));              
            }break;
            case 'yyyy/M/d, HH:mm':{
                data=data.replace(" ","");
                d=data.split(',');
                var dt=d[0].split('/'),tm=d[1].split(':');
                return new Date(parseInt(dt[0],10),parseInt(dt[1],10)-1,parseInt(dt[2],10),parseInt(tm[0],10),parseInt(tm[1],10));              
            }break;
            case 'M-d-yyyy, h:mm a':{
                var a=data.substring(data.length-2,data.length);
                data=data.substring(0,data.length-3);
                data=data.replace(" ","");
                d=data.split(',');
                var dt=d[0].split('-'),tm=d[1].split(':');
                var h=parseInt(tm[0],10),m=parseInt(tm[1],10);
                if(a.toUpperCase()=='PM'&&h!=12){
                    h=h+12;
                }
                return new Date(parseInt(dt[2],10),parseInt(dt[0],10)-1,parseInt(dt[1],10),h,m);
            }break;
            case 'M/d/yyyy, h:mm a':{
                var a=data.substring(data.length-2,data.length);
                data=data.substring(0,data.length-3);
                data=data.replace(" ","");
                d=data.split(',');
                var dt=d[0].split('/'),tm=d[1].split(':');
                var h=parseInt(tm[0],10),m=parseInt(tm[1],10);
                if(a.toUpperCase()=='PM'&&h!=12){
                    h=h+12;
                }
                return new Date(parseInt(dt[2],10),parseInt(dt[0],10)-1,parseInt(dt[1],10),h,m);
            }break;
            case 'yyyy-M-d, h:mm a':{
                var a=data.substring(data.length-2,data.length);
                data=data.substring(0,data.length-3);
                data=data.replace(" ","");
                d=data.split(',');
                var dt=d[0].split('-'),tm=d[1].split(':');
                var h=parseInt(tm[0],10),m=parseInt(tm[1],10);
                if(a.toUpperCase()=='PM'&&h!=12){
                    h=h+12;
                }
                return new Date(parseInt(dt[0],10),parseInt(dt[1],10)-1,parseInt(dt[2],10),h,m);
            }break;
            case 'yyyy/M/d, h:mm a':{
                var a=data.substring(data.length-2,data.length);
                data=data.substring(0,data.length-3);
                data=data.replace(" ","");
                d=data.split(',');
                var dt=d[0].split('/'),tm=d[1].split(':');
                var h=parseInt(tm[0],10),m=parseInt(tm[1],10);
                if(a.toUpperCase()=='PM'&&h!=12){
                    h=h+12;
                }
                return new Date(parseInt(dt[0],10),parseInt(dt[1],10)-1,parseInt(dt[2],10),h,m);
            }break;
            default:{
                return data;
            }
            break;
        }
    },
    _dataTypeConvert:function(data, type, format){ //type:string(def), date, time, int, float
        type = type||this.cfg.sortDataType;
        if(data===''||data===null){
            return NaN;
        }
        switch(type.toLowerCase()){
            case 'int':{
                return parseInt(data);
            }break;
            case 'float':{
                return parseFloat(data);
            }break;
            case 'time':{
                //return new Date(data + ' ' + (new Date()).toDateString());
                return this._dateTimeConvert(data,format);
            }break;
            case 'date':{
                //return new Date(data);
                return this._dateTimeConvert(data,format);
            }break;
            case 'html':{
                return data.replace(/<a\b[^>]*>[\s\S]*?<\/a>/ig,'');
                //return data.length || 0;
            }break;
            default:{
                return data.toString();
            }break;
        }
    },
    _sortByCell:function(cellKey, order, rowsDOM, container,refetch){
        var self = this;
        //object to array
        var a = [], r = null;
		//sort
        this.lastSortOrder = order;
        this._clearSort();
		rowsDOM.each(function(i){ //use DOMMAP ??
			r = self.rows[this.getAttribute('rowid')];
			r._sortData = self._dataTypeConvert(r.cells[cellKey].getValue(), r.cells[cellKey].dataType || self.cfg.sortDataType, r.cells[cellKey].dataFormat);
			a.push(r);
		});
		
		if (this.lastSortOrder == 'ASC') {
			a.sort(function(row1, row2){
				if (row1.cells[cellKey].customSort) {
					return row1.cells[cellKey].customSort(row1._sortData, row2._sortData);
				}
				else {
					return self._sort(row1, row2, cellKey); //ASC
				}
			});
			this.header.row.cells[cellKey].el.addClass('rtq-grid-cell-sd');
		}
		else {
			a.sort(function(row1, row2){
				if (row1.cells[cellKey].customSort) {
					return row1.cells[cellKey].customSort(row2._sortData, row1._sortData);
				}
				else {
					return self._sort(row2, row1, cellKey, true); //DESC
				}
			});
			this.header.row.cells[cellKey].el.addClass('rtq-grid-cell-sa');
		}
		this.lastSortCellKey = cellKey;
		//re-append
		var frag = document.createDocumentFragment(); //For Performance
		for (var i = 0; i < a.length; i++) {
			frag.appendChild(a[i].el[0]);
		}
		container.appendChild(frag);
    },
    _sort: function(row1, row2, cellKey, isDESC){
        var type = row1.cells[cellKey].dataType || this.cfg.sortDataType;
        var v1 = row1._sortData;
        var v2 = row2._sortData;
        
        switch (type) {
			case 'link'://20111014 kidy solve "<a>AAU.VN</a>" < "<a>AAU</a>"
			    v1=v1.replace(/(<.*?>)/ig,"");
				v2=v2.replace(/(<.*?>)/ig,"");
				break;
			case 'mix'://20120227 kidy add to support ABC 12 > ABC 2
				v1=v1.split(/(\d+)/g);
				v2=v2.split(/(\d+)/g);
				break;
            case 'string':
                v1=v1||"";
                v2=v2||"";
                break;
            default:
                if (isNaN(v1)) {
                    return isDESC ? -1 : 1;
                }
                if (isNaN(v2)) {
                    return isDESC ? 1 : -1;
                }
                break;
        }
        if(type=="mix"){
			var length=Math.min(v1.length,v2.length);
			for(var i=0;i<length;i++){
				var tag1=isNaN(v1[i]),tag2=isNaN(v2[i]);
				if(tag1^tag2){//one is number, another is char
					return !tag1?1:-1;
				}else{//all are char or number
					if(v1[i]==v2[i]){
						if(i==length-1){
							return v1.length==v2.length?0:(v1.length>length?-1:1);
						}
						continue;
					}
					else{
						if(tag1&tag2){//all is char
							return v1[i]<v2[i]?1:-1;
						}else{
							return parseInt(v1[i])<parseInt(v2[i])?1:-1;
						}
					}
				}
			}
		}else{
			return v1 > v2?-1:(v1 == v2?0:1);
		}
    },
    _clearSort: function(){
        if (this.lastSortCellKey && this.header.row.cells[this.lastSortCellKey]) {
            this.header.row.cells[this.lastSortCellKey].el.removeClass('rtq-grid-cell-sd').removeClass('rtq-grid-cell-sa');
        }
    },
    sortByCell:function(cellKey, order,execDefault){
		var colConfig=this.getColumnByKey(cellKey);
		
        if(!this.cfg.headerRow || colConfig.sortable === false){return;}
        
        this.highlightColumn(cellKey, false);
        
        order = order || (this.header.row.cells[cellKey].el.hasClass('rtq-grid-cell-sa')?'ASC':'DESC');
       
        if (!colConfig.refetch||execDefault) {
			if (this.cfg.groupDataIndex) {
			
				for (var g in this.dataGroup.groups) {
					this._sortByCell(cellKey, order, this.dataGroup.groups[g].body.find('.rtq-grid-row'), this.dataGroup.groups[g].body[0]);
				}
			}
			else {
				this._sortByCell(cellKey, order, this._getAllRowsDOM(), this._getBody());
			}
			
			this._setAlternateRow();
			this._trigger('cellsort', [cellKey, order]);
			
		}else{
			this._refetchDataSort(cellKey, order);
		}
        
    },
	_refetchDataSort:function(cellKey,order){
		var self=this;
		this._trigger("sortedcell",[cellKey,order,function(needsort){
			//self.reBindData(data);
			if (needsort) {
				self.sortByCell(cellKey, order,true);
			}else{
				self.setSortICON(cellKey,order == "ASC"?"DESC":"ASC");
			}
		}]);
	},
	setSortICON:function(cellkey,order){
		this.lastSortOrder = order == "ASC"?"DESC":"ASC";
		this._clearSort();
		if (this.header.row.cells[cellkey]) {
			if (order == "ASC") {
				this.header.row.cells[cellkey].el.addClass('rtq-grid-cell-sa');
			}
			else {
				this.header.row.cells[cellkey].el.addClass('rtq-grid-cell-sd');
			}
		}
		this.lastSortCellKey = cellkey;
		
	},
    setColumnCfg:function(cols){
        this.empty();
        this.cfg.column = cols.slice();
        this._createHeader();
    },
    getColumnCfg:function(){
        return this.cfg.column;
    },
    getColumnByKey:function(cellKey){
        for (var i = 0; i <= this.cfg.column.length; i++) {
            if(this.cfg.column[i].dataIndex == cellKey){
                return this.cfg.column[i];
            }
        }
        return {};
    },
    setGroup:function(groupDataIndex){
        this.cfg.groupDataIndex = groupDataIndex;
        this.cfg.htmlLayout = 'div';
        if (!this.dataGroup) {
            this.dataGroup = new Util.dataGroup(this.body, {
                dragable: this.cfg.groupDragable,
                dropable: this.cfg.groupDropable,
                autoSort: this.cfg.groupAutoSort,
                group: 'dataGridGroup'
            }, this);
        }
    },
    showLoading:function(){
        if(!this.loadingIndicator){
            this.loadingIndicator = $('<div class="loading-indicator">').appendTo(this.el);
        }
        this.el.addClass('rtq-grid-loading');
    },
    hideLoading:function(){
        this.el.removeClass('rtq-grid-loading');
    },
    //trigger custom event
    _trigger:function(event, args){
        var cb = null;
        switch(event.toLowerCase()){
            case 'cellsort':
                cb = this.cb.onCellSort;
            break;
			case 'sortedcell':
			     cb = this.cb.onSortedbyCell;
			break;
            case 'rowremove':
                cb = this.cb.onRowRemove;
            break;
            case 'rowrearrange':
                cb = this.cb.onRowRearrange;
            break;
            case 'rowdragstart':
                cb = this.cb.onRowDragStart;
            break;
            case 'rowdoubleclick':
                cb = this.cb.onRowDoubleClick;
            break;
            case 'rowcontextmenu':
                cb = this.cb.onRowContextmenu;
            break;
            case 'rowselected':
                cb = this.cb.onRowSelected;
            break;
            case 'cellrearrange':
                cb = this.cb.onCellRearrange;
            break;
            case 'scroll':
                cb = this.cb.onScroll;
            break;
            case 'scrollend':
                cb = this.cb.onScrollEnd;
            break;
            case 'cellremove':
                cb = this.cb.onCellRemove;
			break;
			case 'afterbind':
				cb = this.cb.afterRebindData;
            break;
        }
        if(cb){
            cb.apply(this, args);
        }
        this.el.trigger(event.toLowerCase(), args);
    },
    bind:function(event, callback){
        var self = this;
        switch(event.toLowerCase()){
            case 'rowdoubleclick':{
                for(var r in this.rows){
                    this.rows[r].el.unbind('dblclick.DataGrid');
                    this.rows[r].el.bind('dblclick.DataGrid', function(e){
                        callback(e, self.rows[r]);
                    });
                }
                this.cb.onRowDoubleClick = callback;
            }break;
            case 'rowcontextmenu':{
                for(var r in this.rows){
                    this.rows[r].el.unbind('contextmenu.DataGrid');
                    this.rows[r].el.bind('contextmenu.DataGrid', function(e){
                        callback(e, self.rows[r]);
                    });
                }
                this.cb.onRowContextmenu = callback;
            }break;
            default:
                this.el.bind(event.toLowerCase(), function(){
                    Array.prototype.splice.call(arguments, 0, 1);//remove Jquery event
                    callback.apply(self, arguments);
                });
            break;
        }
    }
};

Util.DG_emptyGroupKey = '(none)';
Util.dataGroup = function(container, configs, datagrid){
    this.groups = {};
    this.datagrid = datagrid;
    this.cfg = {
        dragable: false,
        dropable: false,
        group: 'dataGridGroup',
        collapsible: true,
        autoSort: true
    };
    $.extend(this.cfg, configs);
    this.container = container;
    this.init();
};
Util.dataGroup.prototype = {
    init: function(){
        
    },
    inGroup: function(value){
        return this.groups[value];
    },
    _getGroupIndex: function(value){ //get group index if autosort = true
        var values = [];
        values.push(value);
        for(var g in this.groups){
            if (this.groups[g]) {
                values.push(this.groups[g].value);
            }else{
                return -1;
            }
        }
        values.sort();
        return $.inArray(value, values);
    },
    _createGroup: function(value, prepend){
        var self = this;
        var g = document.createElement('div');
        g.className = 'rtq-grid-group rtq-grid-group-' + value.toLowerCase().replace(/[^a-z]/g, '_');
        g.innerHTML = '<h3 class="rtq-grid-group-til">' + value + '</h3><div class="rtq-grid-group-bd"></div>';
        
        if (this.cfg.autoSort) {
            var idx = this._getGroupIndex(value);
            var children = this.container.children();
            if (idx > -1 && children.length > 0 && idx < children.length) {
                this.container[0].insertBefore(g, children.eq(idx)[0]);
            }
            else {
                this.container[0].appendChild(g);
            }
        }
        else 
            if (prepend) {
                this.container[0].insertBefore(g, this.container[0].firstChild);
            }
            else {
                this.container[0].appendChild(g);
            }
            
        this.groups[value] = {};
        this.groups[value].value = value;
        this.groups[value].el = $(g);
        this.groups[value].header = this.groups[value].el.children('.rtq-grid-group-til');
        this.groups[value].body = this.groups[value].el.children('.rtq-grid-group-bd');
        
        //event
        this.groups[value].el.hover(function(e){
            $(this).addClass('rtq-grid-group-hover');
        }, function(e){
            $(this).removeClass('rtq-grid-group-hover');
        });
        
        if (this.cfg.dragable) {
            var drag = new Util.Dragable(this.groups[value].header, {
                onDragStart: function(e, proxy){
                    proxy.html(value);
                    proxy.groupObj = self;
                    proxy.dragGroup = self.groups[value];
                    self.groups[value].el.addClass('rtq-grid-group-drag');
                    if(self.datagrid.cb.onGroupDragStart){
                        self.datagrid.cb.onGroupDragStart(proxy, self.groups[value]);
                    }
                },
                onDragEnd: function(e, proxy){
                    proxy.dragGroup = null;
                    if (self.groups[value]) {
                        self.groups[value].el.removeClass('rtq-grid-group-drag');
                    }
                }
            }, {
                group: this.cfg.group
            });
        }
        
        if (this.cfg.dropable) {
            var drop = new Util.Dropable(this.groups[value].el, {
                onEnter: function(e, proxy){
                    self.groups[value].el.addClass('rtq-grid-group-drop');
                },
                onDrop: function(e, proxy){
                    if(self.datagrid.cb.beforeGroupDrop){
                        self.datagrid.cb.beforeGroupDrop(proxy, proxy.dragGroup, self.groups[value]);
                    }
                    if (proxy.groupObj && proxy.groupObj == self && !proxy._stopEvent) {
                        self.insertBefore(proxy.dragGroup, self.groups[value]);
                    }
                    if (self.groups[value]) {
                        self.groups[value].el.removeClass('rtq-grid-group-drop');
                    }
                    if(self.datagrid.cb.afterGroupDrop){
                        self.datagrid.cb.afterGroupDrop(proxy, proxy.dragGroup, self.groups[value]);
                    }
                },
                onOut: function(e, proxy){
                    self.groups[value].el.removeClass('rtq-grid-group-drop');
                }
            }, {
                group: this.cfg.group
            });
        }
        
        if (this.cfg.collapsible) {
            var a = document.createElement('a');
            a.className = 'rtq-grid-group-tool-clp';
            a.innerHTML = 'collapsible';
            $(a).appendTo(this.groups[value].header).click(function(){
                self.groups[value].el.toggleClass('rtq-grid-group-clp');
                return false;
            });
        }
        
        return this.groups[value];
    },
    insertBefore:function(group, beforeGroup){
        this.container[0].insertBefore(group.el[0], beforeGroup.el[0]);
    },
    getGroupBox: function(value, prepend){
        value = typeof value == 'undefined' || value.length<1 ? Util.DG_emptyGroupKey : value;
        if(this.groups[value]){
            return this.groups[value];
        }else{
            return this._createGroup(value, prepend);
        }
    },
    getIDs: function(groupKey){
        var a = [];
        var self = this;
        if (this.groups[groupKey]) {
            this.groups[groupKey].el.find('.rtq-grid-row').each(function(i){
                a.push(this.getAttribute('rowid'));
            });
        }
        return a;
    },
    _removeGroup:function(groupKey){
        this.groups[groupKey].el.remove();
        this.groups[groupKey] = null;
        delete this.groups[groupKey];
    },
    removeGroup:function(groupKey){
        var ids = this.getIDs(groupKey);
        for (var i = 0; i < ids.length; i++) {
            this.datagrid.removeRow(ids[i]);
        }
        this._removeGrouop(groupKey);
    },
    length:function(){
        var i = 0;
        for(var g in this.groups){
            i++
        }
        return i;
    },
    empty:function(){
        this.groups = {};
    }
};


Util.GridHeader = function(grid, callbacks){
    this.grid = grid;
    this.el = null;
    this.row = null;
    this.resizeProxy = null;
    this.colSortProxy = null;
    this.colInsertTip = null;
    this.sorting = -1;
    this.sortTimer = null;
    this.appendToCell_el = null;
    this.insertToIdx = 0;
    this.cb = callbacks || {}; //afterResizeCol, afterColSort, onCellClick
    this.columnCfg = grid.cfg.column;
    this.cfg = {
        cellResize: grid.cfg.cellResize,
        cellRearrange: grid.cfg.cellRearrange
    }
    this.htmlTag = this.grid.cfg.htmlLayout == 'table'?'tr':'div';
    this.init();
}
Util.GridHeader.prototype = {
    init: function(){
        var self = this;
        this.el = this.grid.headerEl;
        var data = {}, cols = [];
        //create header row column
        var showColCount = 0;
        for (var i = 0, l = this.columnCfg.length; i < l; i++) {
            data[this.columnCfg[i].dataIndex] = this.columnCfg[i].header;
            var c = {};
            for(var k in this.columnCfg[i]){
                c[k] = this.columnCfg[i][k];
                if(k == 'renderer'){
                    c[k] = null;
                }
            }
            if(c.hide !== true){
                showColCount++;
            }
            cols.push(c);
        }
        if (showColCount <= 1) {
            this.cfg.cellResize = false;
            this.cfg.cellRearrange = false;
        }
        
        if (this.grid.cfg.htmlLayout == 'table') {
            var table = $('<table class="rtq-grid-table"><tbody></tbody></table>');
            this.row = new Util.DataRow(table.children('tbody')[0], cols, data, null, null, this.htmlTag);
            this.row.el.addClass('rtq-grid-rzrow');
            //table.style.width = this.row.getRowWidth() + 'px';
            this.el.append(table);
        }else{
            this.row = new Util.DataRow(this.el, cols, data, null, null, this.htmlTag);
            this.row.el.addClass('rtq-grid-rzrow');
        }
        for (var k in this.row.cells) {
            var cell = self.row.cells[k];
            cell.el[0].cellKey = cell.key;
        }
        
        //Cell Resize
        if (this.cfg.cellResize) {
            this.resizeProxy = $('<div class="rtq-grid-rzproxy">').appendTo(this.grid.el);
        }
        //Cell Rearrange
        if (this.cfg.cellRearrange) {
            this.colSortProxy = $('<div class="rtq-grid-csproxy">').appendTo(this.grid.el);
            this.colInsertTip = $('<div class="rtq-grid-citip">').appendTo(this.grid.el);
        }
        
        //event
        for (var k in this.row.cells) {
            (function(i){
                var cell = self.row.cells[i];
                //Cell Resize
                if (self.cfg.cellResize) {
                    cell.resizeTg = $('<div class="rtq-grid-rztg">').html('').appendTo(cell.el.children('div'));
                }
                
                cell.el.mousedown(function(e){
					if($.browser.safari&&e.button!=0) return false;//kidy 20110921-safari 5.1 don't support mouseup event on right mouse key
                    var target = $(e.target);
                    if(target.hasClass('rtq-grid-rztg')){
                        //Cell resize
                        self.onResizeDragStart(e, cell);
                    }else{
                        //Cell Rearrange
                        if (self.cfg.cellRearrange) {
                            e.preventDefault(); // jQuery bug in FF/IE ??
                            self.sortTimer = setTimeout(function(){
                                self.onColSortDragStart(e, cell);
                            }, 300);
                        }
                    }
                }).mouseup(function(e){
					if($.browser.safari&&e.button!=0) return false;//kidy 20110921-safari 5.1 don't support mouseup event on right mouse key 
                    clearTimeout(self.sortTimer);
                    self.sortTimer = -1;
                }).mousemove(function(e){
                    self.onColumnMousemove(e, cell);
                }).mouseover(function(e){
                    $(this).addClass('rtq-grid-cell-over');
                    if(self.grid.cfg.highlightColumn && !$(this).hasClass('onsort')){
                        self.grid.highlightColumn(cell.key, true);
                    }
                }).mouseout(function(e){
                    $(this).removeClass('rtq-grid-cell-over');
                    if(self.grid.cfg.highlightColumn && !$(this).hasClass('onsort')){
                        self.grid.highlightColumn(cell.key, false);
                    }
                }).click(function(e){
					var target = $(e.target);
					if(self.cb.onCellClick &&!target.hasClass('rtq-grid-rztg')){ 
						self.cb.onCellClick(cell.key);
					}
                });
            })(k);
        }
    },
	ellipsisDom:function(cellobj){
        //ellipsis some char
		if (typeof cellobj == 'undefined') {
			for (var i = 0, l = this.columnCfg.length; i < l; i++) {
				var col = this.columnCfg[i];
				if (col.ellipsis) {
					var cell = this.row.cells[col["dataIndex"]];
					cell.el.ellipsis(false, 1);
					if (!jQuery.isSupportTextoverflow&&this.cfg.cellResize) {
						cell.resizeTg = $('<div class="rtq-grid-rztg">').html('').appendTo(cell.el.children('div'));
					}
				}
			}
		}else{
			cellobj.el.ellipsis(false, 1);
			if (!jQuery.isSupportTextoverflow&&this.cfg.cellResize) {
				cellobj.resizeTg = $('<div class="rtq-grid-rztg">').html('').appendTo(cellobj.el.children('div'));
			}
		}
    },
    _getDomIndex:function(el){
        return el.parent().children().index(el);
    },
    onResizeDragStart: function(e, cellObj){
        //e.preventDefault(); //enable the dragging outside of window/iframe in IE/Chrome
        var self = this;
        cellObj.left = cellObj.el.offset().left;
        this.resizeProxy.css({
            left: cellObj.left - this.el.offset().left,// + this.el.scrollLeft(),
            width: cellObj.el.width()
        }).show();
        
        //maximum width of the column
        if (this.grid.cfg.autoWidth) {
            this.resizeProxy._maxWidth = cellObj.getWidth() + this.row.getNextCell(cellObj).getWidth() - this.grid.cfg.colMinWidth;
        }else{
            this.resizeProxy._maxWidth = 500;
        }
        
        var target = e.target;
        $(document).bind('mousemove.DataGrid', function(e){
            self.onResizeDraging(e, cellObj);
        }).bind('mouseup.DataGrid', function(e){
            if($.browser.safari&&e.button!=0) return false;//kidy 20110921-safari 5.1 don't support mouseup event on right mouse key
            //enable text selection in IE
            if (target.releaseCapture) {
                document.body.unselectable = '';
                document.body.onselectstart = null;
                target.releaseCapture();
            }
            //enable text selection in FF/Chrome
            target.style.userSelect = target.style.MozUserSelect = '';
            
            self.onResizeEnd(e, cellObj);
        });
        
        //disable text selection in IE
        if (target.setCapture) {
            document.body.unselectable = "on";
            document.body.onselectstart = function(){
                return false
            };
            target.setCapture();
        }
        //disable text selection in FF/Chrome
        target.style.userSelect = target.style.MozUserSelect = "none";
    },
    onResizeDraging:function(e, cellObj){
        e.preventDefault();
        var w = Math.min(e.clientX - cellObj.left, this.resizeProxy._maxWidth);

        this.resizeProxy.css({
            width: w>this.grid.cfg.colMinWidth?w:this.grid.cfg.colMinWidth
        });
        return false;
    },
    onResizeEnd: function(e, cellObj){
        $(document).unbind('mouseup.DataGrid').unbind('mousemove.DataGrid');
        var w = this.resizeProxy.width();//e.clientX - cellObj.left;
        this.resizeProxy.hide();
        w = w > this.grid.cfg.colMinWidth ? w : this.grid.cfg.colMinWidth;
        if (this.grid.cfg.autoWidth) {
            //resize in percentage for autoWidth
            var rowW = this.row.getRowWidth();
            var oldW = cellObj.el.width()/rowW*100;
            var newW = w/rowW*100;
            var nextCell = this.row.getNextCell(cellObj);
            var nextCellWidth = (nextCell.el.width()/rowW*100 - newW + oldW) + '%';
            nextCell.resize(nextCellWidth);
            this.grid.resizeCol(nextCell.key, nextCellWidth);
            w = newW + '%';
        }
		var self=this;
        cellObj.resize(w,null,function(){
			if(cellObj.ellipsis){
				self.ellipsisDom(cellObj);
			}
		});
        if (this.cb.afterResize) {
            this.cb.afterResize(cellObj.key, w);
        }
        return false;
    },
    onColSortDragStart: function(e, cellObj){
        //e.preventDefault(); // jQuery bug in FF/IE ??
        var self = this;
        cellObj.el.addClass('onsort');
        this.grid.highlightColumn(cellObj.key, true);
        cellObj.left = cellObj.el.offset().left;
        cellObj.dragX = this.el.offset().left - 20;// - this.el.scrollLeft();
        this.colSortProxy.css({
            left: e.clientX - cellObj.dragX,
            width: cellObj.el.width()
        }).html(cellObj.getValue()).show();
        
        $(document).bind('mousemove.DataGrid', function(e){
            self.onColSortDraging(e, cellObj);
        }).bind('mouseup.DataGrid', function(e){
            if($.browser.safari&&e.button!=0) return false;//kidy 20110921-safari 5.1 don't support mouseup event on right mouse key
            self.onColSortEnd(e, cellObj);
        });
        
        this._setCellsIndex();
        this.sorting = cellObj.el[0].idx;
        this.insertToIdx = cellObj.el[0].idx;
    },
    onColSortDraging:function(e, cellObj){
        e.preventDefault();
        this.colSortProxy.css({
            left: e.clientX - cellObj.dragX
        });
        return false;
    },
    onColSortEnd: function(e, cellObj){
        $(document).unbind('mouseup.DataGrid').unbind('mousemove.DataGrid');
        this.colSortProxy.hide();
        this.colInsertTip.hide();
        cellObj.el.removeClass('onsort');
        this.grid.highlightColumn(cellObj.key, false);
        this.sorting = -1;
        
        if (this.insertToIdx>-1 && this.insertToIdx != cellObj.el[0].idx) {
            this.appendCellTo(cellObj.key, this.insertToIdx);
            
            if(this.cb.afterColSort){
                this.cb.afterColSort(cellObj.key, this.insertToIdx);
            }
        }
        return false;
    },
    appendCellTo: function(cellKey, index){
        this.row.appendCellTo(cellKey, index);
        this._setCellsIndex();
    },
    _setCellsIndex:function(){
        this.el.find('.rtq-grid-cell').each(function(i){
            this.idx = i;
        });
    },
    onColumnMousemove: function(e, cellObj){
        e.preventDefault();
        if (this.sorting > -1) {
            
            if(this.sorting == cellObj.el[0].idx){
                this.insertToIdx = -1;
                this.colInsertTip.hide();
                return;
            }
            
            var eLeft = e.clientX;
            var offsetLeft = cellObj.el.offset().left;
            var offsetWidth = cellObj.el.width();
            var offsetMiddle = offsetLeft + offsetWidth / 2;
            
            this.insertToIdx = cellObj.el[0].idx;
            if (this.sorting == this.insertToIdx + 1) { //insert before left el
                this.colInsertTip.css({
                    left: offsetLeft - this.el.offset().left
                }).show();
            }
            else 
                if (this.sorting == this.insertToIdx - 1) { //insert after right el
                    this.colInsertTip.css({
                        left: offsetLeft - this.el.offset().left + offsetWidth
                    }).show();
                    this.insertToIdx++;
                }
                else 
                    if (eLeft > offsetMiddle) {
                        this.colInsertTip.css({
                            left: offsetLeft - this.el.offset().left + offsetWidth
                        }).show();
                        this.insertToIdx++;
                    }
                    else {
                        this.colInsertTip.css({
                            left: offsetLeft - this.el.offset().left
                        }).show();
                    }
        }
    },
    clear: function(){
        this.el.empty();
        for (var p in this) {
            if (!p || p == 'clear') {
                continue;
            }
            this[p] = null;
            delete this[p];
        }
    }
}
/*data row*/
Util.DataRow = function(container, column, data, rowid, delBtnClick, htmlTag, prepend, delayRender, callback){
    this.dom = null;
    this.el = null;
    this.container = container[0]?container[0]:container;
    this.column = column;
    this.id = rowid;
    this.index = 0;
    this.data = data;
    this.delayRender = delayRender;
    this.rendered = false;
    this.prepend = prepend || false; 
    this.cfg = {};
    this.delBtnClick = delBtnClick || function(){};
    callback = callback || {};
    this.callback = {
        afterRender: callback.afterRender,
        mouseout: callback.mouseout,
        mouseover: callback.mouseover,
        cellMouseover: callback.cellMouseover,
        cellMouseout: callback.cellMouseout,
		cellClick: callback.cellClick
    };
    this.cells = {};
    this.DOMMap = {};
    this.htmlTag = htmlTag == 'div' ?'div':'tr';
    this.init();
};
Util.DataRow.prototype={
    init:function(){
        //var s = (new Date()).getTime();
        var self = this;
        this.dom = document.createElement(this.htmlTag);
        this.dom.className = 'rtq-grid-row';
        this.dom.setAttribute('rowid', this.id||'');
        //this.el = $(this.HTML).attr('rowid', this.id||''); //+20% Pfm
        
        //this.el.appendTo(this.container);
        if (this.prepend) {
            this.container.insertBefore(this.dom, this.container.firstChild);
        }else{
            this.container.appendChild(this.dom);
        }
        this.el = $(this.dom);
        
        if (!this.delayRender) {
            this.renderDOM();
        }        
        
    },
    renderDOM:function(){
        if (!this.rendered) {
            var self = this;
            var cell, col, frag = document.createDocumentFragment(),$cells=[];
            for (var i = 0, l = this.column.length; i < l; i++) {
                col = this.column[i];
                cell = new Util.DataCell({
                    text: this.data[col.dataIndex],
                    width: col.width,
                    align: col.align,
                    hide: col.hide,
                    dataType: col.dataType,
                    type: col.type,
                    renderer: col.renderer,
                    className: col.className,
                    customSort: col.customSort,
                    dataFormat: col.dataFormat,
					ellipsis: col.ellipsis
                }, null, this.htmlTag);
                cell.key = col.dataIndex;
				
                cell.dom.cellKey = col.dataIndex;
                //this.el.append(cell.el);
                frag.appendChild(cell.dom);
                this.cells[col.dataIndex] = cell;
                this.DOMMap[col.dataIndex] = cell.innerDom;
                if (col.type == 'remove') {
                    if (col.confirm === true && Util.confirm) {
                        cell.onClick = function(){
                            Util.confirm("Are you sure you want to delete?", function(b){
                                if (b == true) {
                                    self.delBtnClick(self.id);
                                }
                            });
                        }
                    }else{
                        cell.onClick = function(){
                            self.delBtnClick(self.id);
                        };
                    }
                }
            }
            this.dom.appendChild(frag); // +25% Pfm
            //event
            this.el.mouseover(function(e){
                if(!self.el) return;
                self.el.addClass('rtq-grid-row-over');
                if (self.callback.mouseover) {
                    self.callback.mouseover(e, self);
                }
            }).mouseout(function(e){
                if(!self.el) return;
                self.el.removeClass('rtq-grid-row-over');
                if (self.callback.mouseout) {
                    self.callback.mouseout(e, self);
                }
            });
            
            if(this.callback.cellMouseover){
                for(var c in this.cells){
                    with ({
                        c: c
                    }) {
                        this.cells[c].el.mouseover(function(e){
                            self.callback.cellMouseover(e, self.cells[c], self);
                        });
                    }
                }
            }
            if(this.callback.cellMouseout){
                for(var c in this.cells){
                    with ({
                        c: c
                    }) {
                        this.cells[c].el.mouseout(function(e){
                            self.callback.cellMouseout(e, self.cells[c], self);
                        });
                    }
                }
            }
            if(this.callback.cellClick){
                for(var c in this.cells){
					if(c=="remove") continue;//kidy 20120103 solve "remove" cell is bound mutil-event handle
                    with ({
                        c: c
                    }) {
                        this.cells[c].el.click(function(e){
                            self.callback.cellClick(e, self.cells[c], self);
                        });
                    }
                }
            }
			if(this.callback.cellDoubleClick){
                for(var c in this.cells){
					if(c=="remove") continue;//kidy 20120103 solve "remove" cell is bound mutil-event handle
                    with ({
                        c: c
                    }) {
                        this.cells[c].el.dblclick(function(e){
                            self.callback.cellDoubleClick(e, self.cells[c], self);
                        });
                    }
                }
            }
            this.rendered = true;
        }
    },
    remove:function(){
        this.el.remove();
        this.clear();
    },
    resizeCell:function(cellKey, w, h){
        if (this.rendered) {
            this.cells[cellKey].resize(w, h);
        }
    },
    removeCell:function(cellKey){
        this.cells[cellKey].remove();
        this.cells[cellKey] = null;
        delete this.cells[cellKey];
    },
    appendCellTo:function(cellKey, index){
        if (this.rendered) {
            var children = this.el.children();
            if (children.length > index) {
                children.eq(index).before(this.cells[cellKey].el);
            }
            else {
                this.el.append(this.cells[cellKey].el);
            }
        }
    },
    getRowWidth:function(){
        var w = 0;
        for(var k in this.cells){
            w += this.cells[k].getWidth();
        }
        return w;
    },
    getNextCell:function(cellObj){
        var self = this;
        var find = function(cell, direction){ //direction:next/prev
            var nextCell = cell.el[direction]()[0];
            if (nextCell) {
                if (self.cells[nextCell.cellKey].hide == true) {
                    return arguments.callee(self.cells[nextCell.cellKey], direction);
                }
                else {
                    return self.cells[nextCell.cellKey];
                }
            }else{
                return null;
            }
        };
        var nextCell = find(cellObj, 'next');
        if (nextCell) {
            return nextCell;
        }else{
            return find(cellObj, 'prev');
        }
    },
    clear:function(){
        for(var k in this.cells){
            this.cells[k].clear();
        }
        for(var p in this){
            if(!p || p == 'clear'){continue;}
            this[p] = null;
            delete this[p];
        }
    }
};

/*data cell*/
//Util.DataCell = function(text, width, align, hide, dataType, type, renderer, callbacks, htmlTag, className){
//config: text, width, align, hide, dataType, type, renderer, customSort, dataFormat
Util.DataCell = function(config, callbacks, htmlTag){
    this.dom = null;
    this.innerDom = null;
    this.el = null;
	this.originalText=config.text;
    this.text = config.renderer ? (typeof config.renderer == 'function' ? config.renderer(config.text||'') : config.renderer.replace(/{d}/g, config.text||'')) : (config.text || '');
    this.type = config.type||'normal';
    this.align = config.align;
    this.width = isNaN(config.width)?config.width:(config.width > -1)?config.width+'px':null;
    this.height = -1;
	this.ellipsis = config.ellipsis||false;
    this.hide = config.hide || false;
    this.className = config.className || '';
    this.dataType = config.dataType||'';
    this.dataFormat=config.dataFormat||null;
    this.customSort = config.customSort||null;
    this.onClick = callbacks?callbacks.onClick:function(){};
    this.htmlTag = htmlTag=='div'?'div':'td';
    this.init();
};
Util.DataCell.prototype={
    init:function(){
    
        //this.HTML = '<div class="rtq-grid-cell" style="'+this.widthStr+this.alignStr+'"><div class="rtq-grid-cell-ctn">' + this.text + '</div></div>';
        // +50% Pfm
        this.dom = document.createElement(this.htmlTag);
        this.dom.className = 'rtq-grid-cell ' + this.className;
        if(this.hide){
            this.dom.className += ' rtq-grid-cell-hide';
        }
        this.dom.style.cssText = ((this.width)?'width:'+ this.width +';':'') + (this.align?'text-align:' + this.align + ';':'');
        
        this.innerDom = document.createElement('div');
        this.innerDom.className = 'rtq-grid-cell-ctn';
        this.innerDom.innerHTML = this.text;
        
        this.dom.appendChild(this.innerDom);
        this.el = $(this.dom);
        //this.innerDom = this.el.children('div')[0]; //-30% Pfm
        if(this.type=='remove'){
            var self = this;
            this.dom.className += ' rtq-grid-cell-del';
            this.el.click(function(e){
                self.onClick();
            });
        }
    },
    resize:function(w,h,callback){
        this.width = w||this.el.width();
        this.height = h||this.el.height();
        //prevent 0px if can not get width/height
        var css = {};
        if(this.width){
            css.width = this.width;
        }
        if(this.height){
            css.height = this.height;
        }
        this.el.css(css);
		if (typeof callback == 'function') {
			callback();
		}else{
			this.ellipsisDom();
		}
    },
	ellipsisDom:function(){
        //ellipsis some char
		if (this.ellipsis) {
			//this.innerDom.innerHTML = this.text;
			this.el.ellipsis(false);
		}
    },
	updateCell:function(html){
		this.innerDom.innerHTML=html;
		this.ellipsisDom();
	},
    getWidth:function(){
        if (this.hide) {
            return 0;
        }
        else {
            var w = this.el.width();
            return this.htmlTag == 'div' ? w + 1 : w; //1px border
        }
    },
    getValue:function(){
        return this.el.children('div').html();
    },
    remove:function(){
        this.el.remove();
        this.clear();
    },
    clear:function(){
        for(var p in this){
            if(!p || p == 'clear'){continue;}
            this[p] = null;
            delete this[p];
        }
    }
};

Util.DataGridAddOn = {
    HeaderContextmenu: function(grid){
        var currentCellKey = '';
        grid.HeaderCtxMenu = new Util.CascadeMenu(grid.el, {
            display: false,
            autoHide: true,
            isTraceMouse: true
        }, [{
            txt: 'Auto Width',
            val: 'autowidth'
        }, {
            txt: 'Sort',
            val: 'sort'
        }, {
            txt: 'Remove',
            val: 'remove'
        }], {
            onClick: function(item){
                switch (item.data.val) {
                    case 'autowidth':
                        var longestText = '';
                        var text = '';
                        var style = '';
                        for (var i in grid.rows) {
                            text = grid.rows[i].cells[currentCellKey].getValue();
                            if(text.length > longestText.length){
                                longestText = text;
                                style = grid.rows[i].cells[currentCellKey].dom.style.cssText;
                            }
                        }
                        var headerText = grid.header?grid.header.row.cells[currentCellKey].el.text():'';
                        if (longestText.length < headerText.length) {
                            longestText = headerText;
                        }
                        var textContainer = $('<div style="visibility:hidden;position:absolute;top:0;left:0;">'+longestText+'</div>').appendTo(document.body);
                        var width = textContainer.width() + 10;
                        textContainer.remove();
                        grid.header.row.cells[currentCellKey].resize(width);
                        grid.resizeCol(currentCellKey, width);
                    break;
                    case 'sort':
                        grid.sortByCell(currentCellKey);
                    break;
                    case 'remove':
                        grid.removeColumn(currentCellKey);
                    break;
                }
                grid.HeaderCtxMenu.hide();
            }
        });
        
        //bind event
        for (var i in grid.header.row.cells) {
            (function(i){
                var cell = grid.header.row.cells[i];
                cell.el.bind('contextmenu.DataGrid', function(e){
                    grid.HeaderCtxMenu.show(e);
                    currentCellKey = cell.key;
                });
            })(i);    
        }
    }
};





